<?php
// +----------------------------------------------------------------------
// | H1CMS © OpenSource CMS
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2016 http://www.h1cms.com All rights reserved.
// | Copyright (c) 2014-2016 嘉兴领格信息技术有限公司，并保留所有权利。
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Allen <allen@lg4.cn>
// +----------------------------------------------------------------------

define('LG_VERSION', '1.0.2');

define('DS', DIRECTORY_SEPARATOR);

// 常用变量

define('ROOT_DIR', dirname(dirname(__DIR__)) . DS);

define('SYSTEM_DIR', ROOT_DIR . 'system' . DS);

define('STORAGE_DIR', ROOT_DIR . 'storage' . DS);

define('CACHE_DIR', ROOT_DIR . 'storage' . DS . 'cache' . DS);

define('LANGUAGES_DIR', ROOT_DIR . 'storage' . DS . 'languages' . DS);

define('CONFIG_DIR', ROOT_DIR . 'storage' . DS . 'config' . DS);

define('VENDOR_DIR', ROOT_DIR . '/vendor/');

LG::setAlias('@base', ROOT_DIR);
LG::setAlias('@system', SYSTEM_DIR);
LG::setAlias('@storage', STORAGE_DIR);
LG::setAlias('@cache', CACHE_DIR);
LG::setAlias('@language', LANGUAGES_DIR);
LG::setAlias('@config', CONFIG_DIR);
LG::setAlias('@vendor', VENDOR_DIR);

use Lg\Support\Arr;
use Lg\Support\Str;

if (\Lg\Config::get('config.debug', false)) {
//    $whoops = new \Whoops\Run;
//    $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
//    if (\Whoops\Util\Misc::isAjaxRequest()) {
//        $run->pushHandler(new \Whoops\Handler\JsonResponseHandler);
//    }
//    $whoops->register();
    \Symfony\Component\Debug\ErrorHandler::register();
    \Symfony\Component\Debug\Debug::enable();
}
/**
 *
 * @return \h1cms\system\Screen
 */
function screen() {
	return \h1cms\H1::$app->screen;
}

/**
 * @param $key
 * @return \h1cms\H1
 */
function h1($key = null) {
	if (is_string($key)) {
		return \h1cms\H1::$app->{$key};
	}
	return \h1cms\H1::$app;
}

/**
 * 获取单个属性
 * @param string $option
 * @param mixed $default
 * @return mixed
 */
function get_option($option, $default = null) {
	return Lg\Options::get($option, $default);
}

/**
 * 更新属性组 (如果不存在则创建)
 * @param type $option
 * @param type $value
 */
function update_option($option, $value = false) {
	Lg\Options::set($option, $value);
}

/**
 * 删除Option
 * @param string $option 属性组|属性组.属性名
 */
function remove_option($option) {
	Lg\Options::remove($option);
}

// +----------------------------------------------------------------------
// | Flash Helpers
// +----------------------------------------------------------------------
function add_flash($type, $message) {
	Lg\Http\Session::addFlash($type, $message);
}

function get_flash($type, $default = []) {
	return Lg\Http\Session::getFlash($type, $default);
}

function has_flash($type) {
	return Lg\Http\Session::hasFlash($type);
}

/**
 * 获取配置
 * @param string $name
 * @param mixed $default
 * @return mixed
 */
function config($name, $default = null) {
	return Lg\Config::get($name, $default);
}

/**
 * 获取Options
 * @param type $group_name
 * @return array
 */
function lg_load_options($group_name = 'system') {
	$options = \Lg\Database\DB::getConnection()->fetchPairs('options', 'option_name, option_value', ['option_group' => $group_name]);
	return $options;
}

function lg_refresh_cache_options($group_name = 'system') {
	if (is_string($group_name)) {
		$group_name = [$group_name];
	}
	foreach ($group_name as $name) {
		Lg\Options::clear($name);
		Lg\Options::load($name);
	}
}

function lg_remove_options_cache($group_name = 'system') {
	if (is_array($group_name)) {
		foreach ($group_name as $name) {
			remove_cache('_lg_options_' . $name);
		}
		return;
	}
	remove_cache('_lg_options_' . $group_name);
}

function remove_cache($name, $driver = null) {
	if ($driver == NULL) {
		$driver = config('cache.default', 'file');
	}
	$item = Lg\Cache\Cache::disk($driver)->getItem($name);
	$item->clear();
}

/**
 * 系统维护模式
 */
function lg_maintenance() {
	if (!file_exists(STORAGE_DIR . '.maintenance')) {
		return;
	}
//    header("HTTP 503 Service Unavailable", true, 503);
	header('Content-Type: text/html; charset=utf-8');
	header('Retry-After: 600');
	$content = file_get_contents(STORAGE_DIR . '.maintenance');
	if (!$content) {
		$content = __('系统正在维护，请稍后在访问');
	}
	?>
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml" >
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
            <title><?php echo __('系统维护'); ?></title>

        </head>
        <body>
            <h1><?php echo $content; ?></h1>
        </body>
    </html>
    <?php
die();
}

function is_cli() {
	return (php_sapi_name() === 'cli');
}

/**
 * 检测数组是否是 Key : Value
 * @param type $arr
 * @return bool
 */
function is_assoc($arr) {
	return (is_array($arr) && isset($arr[0]) && is_string($arr));
}

// +----------------------------------------------------------------------
// | mobile
// +----------------------------------------------------------------------
function lg_is_mobile() {

}

// +----------------------------------------------------------------------
// | i18N
// +----------------------------------------------------------------------
function __($message, $args = [], $options = []) {
	return \Lg\I18n\Translation::translate($message, $args, $options);
}

function t($message, $args = [], $options = []) {
	return \Lg\I18n\Translation::translate($message, $args, $options);
}

function load_language($name) {
	return \Lg\I18n\Language::loadLanguage($name);
}

// +----------------------------------------------------------------------
// | url helper
// +----------------------------------------------------------------------

/**
 * 获取server BASEURL
 * @param string $path
 * @return string
 */
function root_url($path = null) {
	$baseUrl = stristr(Lg\Http\Request::classic()->getBaseUrl(), '/' . basename(APP_PATH), true);
	return $baseUrl . $path;
}

/**
 * Base Url
 * @param string $path
 * @return string
 */
function base_url($path = null, $webroot = false) {
	$baseUrl = LG::basePath();
	if ($webroot && defined('BASEURL')) {
		$baseUrl = BASEURL;
	}
	return $baseUrl . $path;
}

/**
 * 当前URL
 * @return string
 */
function current_url() {
	return \Lg\Http\Input::req()->url();
}

function get_avatar() {
	return get_default(h1cms\system\Auth::user()->avatar, root_url('/storage/avatar/default.png'));
}

function module_path($path = NULL) {
	$module_name = module_name();
	$module_path = APP_PATH . value_condition($module_name, '/' . $module_name, NULL);
	if (!is_null($path)) {
		$module_path .= '/' . $path;
	}
	return $module_path;
}

/**
 * 获取模块路径
 * @param string $module_name 模块名称
 * @return string
 */
function get_module_path($module_name) {
	return APP_PATH . '/' . $module_name;
}

/**
 * 获取模块URL
 * @param string $path
 * @param string| $query
 * @return string
 */
function module_url($path = null, $query = null) {
	$module_name = module_name();
	$module_url = base_url(value_condition($module_name, '/' . $module_name));
	if (!is_null($path)) {
		$module_url .= '/' . $path;
	}
	if (is_array($query) && !empty($query)) {
		$module_url .= '?' . http_build_query($query);
	}
	return $module_url;
}

function module_name() {
	return ltrim(str_ireplace(base_url(), '', \Lg\Http\Input::basePath()), '/');
}

/**
 * 生成URL
 * @param string $path
 * @param array|string $params
 * @param bool $absoluteUrl
 * @return string
 */
function url_for($path, $params = array(), $absoluteUrl = false) {
	$query_string = '';
	if (!empty($params) && is_array($params)) {
		$query_string = '?' . http_build_query($params);
	} else if (is_scalar($params)) {
		$query_string = $params;
	}
	if ($absoluteUrl) {
		return get_option('system.siteurl') . base_url($path . $query_string);
	}
	return base_url($path . $query_string);
}

function redirect_to($path, $params = array(), $redirect = false) {
	$query_string = '';
	if (is_bool($params) && $params && $redirect) {
		lg_maybe_redirect();
	} else if (!empty($params) && is_array($params)) {
		$query_string = '?' . http_build_query($params);
	}
	return lg_redirect(base_url($path . $query_string));
}

/**
 * 完成操作后跳转到 redirect指向的URL
 * @param string $name 跳转URL名字
 */
function lg_maybe_redirect($name = 'redirect') {
	$redirect_url = \Lg\Http\Input::get($name, \Lg\Http\Session::getPreviousUrl());
	if (!empty($redirect_url)) {
		redirect_to($redirect_url);
	}
}

/**
 * redirect
 * @param string $to
 * @param int $code
 */
function lg_redirect($to, $code = 302) {
	if (headers_sent() === false) {
		set_http_status($code);
		header('Location: ' . $to, true, $code);
	}
	exit();
}

/**
 * Response
 * @param type $content
 * @param type $status
 * @param type $headers
 * @return \Lg\Http\Response
 */
function response($content = '', $status = 200, $headers = array()) {
	return new Lg\Http\Response($content, $status, $headers);
}

/**
 *
 * @param string $url
 * @param int $status
 * @param array $headers
 * @return Lg\Support\Redirect
 */
function redirect($url = '', $status = 302, $headers = array()) {
	if (is_array($url)) {
		$_url = Arr::get($url, 0);
		unset($url[0]);
		$url = url_for($_url, $url);
	} else {
		$url = url_for($url);
	}
	return Lg\Support\Redirect::create($url, $status, $headers);
}

/**
 *
 * @return Lg\Session\Session
 */
function session() {
	return Lg\Session\Session::instance();
}

function input_query($key, $default = null) {
	return req()->query->get($key, $default);
}

function input_post($key, $default = null) {
	return req()->request->get($key, $default);
}

function input($key, $default = null) {
	return req()->get($key, $default);
}

function form_has_error($field) {
	return session()->hasFlash('_field_' . $field);
}

function form_error($field, $allError = false) {
	$_ret = session()->getFlash('_field_' . $field, []);
	if (!$allError && isset($_ret[0][0])) {
		return $_ret[0][0];
	} else if (empty($_ret)) {
		return '';
	}
	return isset($_ret[0]) ? $_ret[0] : [];
}

function validation_errors($hasError = false) {
	if ($hasError) {
		return session()->hasFlash('_validation_field_names');
	}
	$_validation_field_names = session()->getFlash('_validation_field_names', []);
	$errors = [];
	foreach ($_validation_field_names as $field_name) {
		$errors[] = session()->getFlash($field_name);
	}
	return $errors;
}

function html_filter($html, $config = null) {
	return HTMLPurifier::instance()->purify($html, $config);
}

/**
 * Http 响应代码
 * @staticvar array $http
 * @param type $code
 */
function set_http_status($code) {
	static $http = array(
		100 => "HTTP/1.1 100 Continue",
		101 => "HTTP/1.1 101 Switching Protocols",
		200 => "HTTP/1.1 200 OK",
		201 => "HTTP/1.1 201 Created",
		202 => "HTTP/1.1 202 Accepted",
		203 => "HTTP/1.1 203 Non-Authoritative Information",
		204 => "HTTP/1.1 204 No Content",
		205 => "HTTP/1.1 205 Reset Content",
		206 => "HTTP/1.1 206 Partial Content",
		300 => "HTTP/1.1 300 Multiple Choices",
		301 => "HTTP/1.1 301 Moved Permanently",
		302 => "HTTP/1.1 302 Found",
		303 => "HTTP/1.1 303 See Other",
		304 => "HTTP/1.1 304 Not Modified",
		305 => "HTTP/1.1 305 Use Proxy",
		307 => "HTTP/1.1 307 Temporary Redirect",
		400 => "HTTP/1.1 400 Bad Request",
		401 => "HTTP/1.1 401 Unauthorized",
		402 => "HTTP/1.1 402 Payment Required",
		403 => "HTTP/1.1 403 Forbidden",
		404 => "HTTP/1.1 404 Not Found",
		405 => "HTTP/1.1 405 Method Not Allowed",
		406 => "HTTP/1.1 406 Not Acceptable",
		407 => "HTTP/1.1 407 Proxy Authentication Required",
		408 => "HTTP/1.1 408 Request Time-out",
		409 => "HTTP/1.1 409 Conflict",
		410 => "HTTP/1.1 410 Gone",
		411 => "HTTP/1.1 411 Length Required",
		412 => "HTTP/1.1 412 Precondition Failed",
		413 => "HTTP/1.1 413 Request Entity Too Large",
		414 => "HTTP/1.1 414 Request-URI Too Large",
		415 => "HTTP/1.1 415 Unsupported Media Type",
		416 => "HTTP/1.1 416 Requested range not satisfiable",
		417 => "HTTP/1.1 417 Expectation Failed",
		500 => "HTTP/1.1 500 Internal Server Error",
		501 => "HTTP/1.1 501 Not Implemented",
		502 => "HTTP/1.1 502 Bad Gateway",
		503 => "HTTP/1.1 503 Service Unavailable",
		504 => "HTTP/1.1 504 Gateway Time-out",
	);
	if (headers_sent() === false && array_key_exists($code, $http)) {
		header($http[$code]);
	}
}

// HTTP
function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered') {

}

/**
 * @param mixed $condition 条件
 * @param mixed $value 值1
 * @param mixed $value 值2
 * @return mixed
 */
function value_condition() {
	$param_num = count(func_get_args());
	if ($param_num == 1) {
		return func_get_arg(0);
	} else if ($param_num == 2) {
		$condition = func_get_arg(0);
		return $condition ? func_get_arg(1) : NULL;
	} else if ($param_num == 3) {
		$condition = func_get_arg(0);
		return $condition ? func_get_arg(1) : func_get_arg(2);
	} else {
		return NULL;
	}
}

function html_escape($var, $double_encode = TRUE) {
	if (empty($var)) {
		return $var;
	}
	if (is_array($var)) {
		foreach (array_keys($var) as $key) {
			$var[$key] = html_escape($var[$key], $double_encode);
		}
		return $var;
	}
	return htmlspecialchars($var, ENT_QUOTES, config_item('charset'), $double_encode);
}

// +----------------------------------------------------------------------
// | File Helper
// +----------------------------------------------------------------------

function mkdir_p($target) {
	$wrapper = null;

	// Strip the protocol.
	if (wp_is_stream($target)) {
		list($wrapper, $target) = explode('://', $target, 2);
	}

	// From php.net/mkdir user contributed notes.
	$target = str_replace('//', '/', $target);

	// Put the wrapper back on the target.
	if ($wrapper !== null) {
		$target = $wrapper . '://' . $target;
	}

	/*
		     * Safe mode fails with a trailing slash under certain PHP versions.
		     * Use rtrim() instead of untrailingslashit to avoid formatting.php dependency.
	*/
	$target = rtrim($target, '/');
	if (empty($target)) {
		$target = '/';
	}

	if (file_exists($target)) {
		return @is_dir($target);
	}

	// We need to find the permissions of the parent folder that exists and inherit that.
	$target_parent = dirname($target);
	while ('.' != $target_parent && !is_dir($target_parent)) {
		$target_parent = dirname($target_parent);
	}

	// Get the permission bits.
	if ($stat = @stat($target_parent)) {
		$dir_perms = $stat['mode'] & 0007777;
	} else {
		$dir_perms = 0777;
	}

	if (@mkdir($target, $dir_perms, true)) {

		/*
			         * If a umask is set that modifies $dir_perms, we'll have to re-set
			         * the $dir_perms correctly with chmod()
		*/
		if ($dir_perms != ($dir_perms & ~umask())) {
			$folder_parts = explode('/', substr($target, strlen($target_parent) + 1));
			for ($i = 1, $c = count($folder_parts); $i <= $c; $i++) {
				@chmod($target_parent . '/' . implode('/', array_slice($folder_parts, 0, $i)), $dir_perms);
			}
		}

		return true;
	}

	return false;
}

function path_is_absolute($path) {

	if (realpath($path) == $path) {
		return true;
	}

	if (strlen($path) == 0 || $path[0] == '.') {
		return false;
	}

	if (preg_match('#^[a-zA-Z]:\\\\#', $path)) {
		return true;
	}

	return ($path[0] == '/' || $path[0] == '\\');
}

function path_join($base, $path) {
	if (path_is_absolute($path)) {
		return $path;
	}
	return rtrim($base, '/') . '/' . ltrim($path, '/');
}

function normalize_path($path) {
	$path = str_replace('\\', '/', $path);
	$path = preg_replace('|(?<=.)/+|', '/', $path);
	if (':' === substr($path, 1, 1)) {
		$path = ucfirst($path);
	}
	return $path;
}

function is_ssl() {
	if (isset($_SERVER['HTTPS'])) {
		if ('on' == strtolower($_SERVER['HTTPS'])) {
			return true;
		}

		if ('1' == $_SERVER['HTTPS']) {
			return true;
		}
	} elseif (isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'])) {
		return true;
	}
	return false;
}

/**
 * 获取变量
 * @param type $value
 * @param type $default
 * @return type
 */
function get_default($value, $default = NULL) {
	if (!empty($value)) {
		return $value;
	}
	return $default;
}

function xmlEncode($mixed, $domElement = null, $DOMDocument = null) {
	if (is_null($DOMDocument)) {
		$DOMDocument = new DOMDocument;
		$DOMDocument->formatOutput = true;
		xmlEncode($mixed, $DOMDocument, $DOMDocument);
		echo $DOMDocument->saveXML();
	} else {
		if (is_array($mixed)) {
			foreach ($mixed as $index => $mixedElement) {
				if (is_int($index)) {
					if ($index === 0) {
						$node = $domElement;
					} else {
						$node = $DOMDocument->createElement($domElement->tagName);
						$domElement->parentNode->appendChild($node);
					}
				} else {
					$plural = $DOMDocument->createElement($index);
					$domElement->appendChild($plural);
					$node = $plural;
					if (!(rtrim($index, 's') === $index)) {
						$singular = $DOMDocument->createElement(rtrim($index, 's'));
						$plural->appendChild($singular);
						$node = $singular;
					}
				}

				xmlEncode($mixedElement, $node, $DOMDocument);
			}
		} else {
			$domElement->appendChild($DOMDocument->createTextNode($mixed));
		}
	}
}

/**
 * array to object
 * @param type $array
 * @return boolean|\stdClass
 */
function arrayToObject($array) {
	if (!is_array($array)) {
		return $array;
	}
	$object = new stdClass();
	if (is_array($array) && count($array) > 0) {
		foreach ($array as $name => $value) {
			$name = strtolower(trim($name));
			if (!empty($name)) {
				$object->$name = arrayToObject($value);
			}
		}
		return $object;
	} else {
		return FALSE;
	}
}

/**
 *
 * @return \Symfony\Component\HttpFoundation\Request
 */
function req() {
	return Lg\Http\Request::classic();
}

// +----------------------------------------------------------------------
// | database helper
// +----------------------------------------------------------------------
/**
 * Get Db Connection
 * @param string $target
 * @return Lg\Database\Database
 */
function db($target = null) {
	return \Lg\Database\Database::getConnection($target);
}

/**
 * Query Sql
 * @param string $query
 * @param array $params
 * @return \PDOStatement
 */
function db_query($query, $params = []) {
	return Lg\Database\Database::getConnection()->query($query, $params);
}

function db_query_value($query, $params = []) {
	return Lg\Database\Database::getConnection()->getValue($query, $params);
}

function db_query_row($query, $params = [], $fetch_style = PDO::FETCH_OBJ) {
	return Lg\Database\Database::getConnection()->getRow($query, $params, $fetch_style);
}

function db_query_cols($query, $params = []) {
	return Lg\Database\Database::getConnection()->getCol($query, $params);
}

function db_insert($table, $columns) {
	return Lg\Database\Database::getConnection()->insert($table, $columns);
}

function db_update($table, $columns, $conditions = '', $params = []) {
	return Lg\Database\Database::getConnection()->update($table, $columns, $conditions, $params);
}

function db_delete($table, $conditions = '', $params = array()) {
	return Lg\Database\Database::getConnection()->delete($table, $conditions, $params);
}

function db_query_dict($table, $columns, $conditions = '', $params = []) {
	return Lg\Database\Database::getConnection()->dict($table, $columns, $conditions, $params);
}

function db_fetch_pairs($table, $columns, $conditions = '', $params = array()) {
	return Lg\Database\Database::getConnection()->fetchPairs($table, $columns, $conditions, $params);
}

function db_seq($name, $remove = false) {
	if ($remove) {
		\Lg\Database\DB::delete('sequences', ['seq_name' => $name]);
		return;
	}
	return Lg\Database\Database::getConnection()->transaction(function ($db) use ($name) {
		$seq_value = $db->getValue('select [value] from {sequences} where [seq_name]=:seq_name FOR UPDATE', ['seq_name' => $name]);
		if ($seq_value !== false) {
			$seq_value++;
			$db->query('UPDATE {sequences} SET [value] =:seq_value  where [seq_name]=:seq_name', ['seq_name' => $name, 'seq_value' => $seq_value]);
		} else {
			$seq_value = 1;
			$db->query('INSERT INTO {sequences} SET [value] = 1,[seq_name]=:seq_name', ['seq_name' => $name]);
		}
		return $seq_value;
	});
}

/**
 * check table exists
 * @param string $table
 * @return boolean
 */
function db_table_exists($table) {
	try {
		db_query("select 1 from {{$table}}");
	} catch (PDOException $exc) {
		return FALSE;
	}
	return true;
}

/**
 *
 * @return int
 */
function db_last_id() {
	return Lg\Database\Database::getConnection()->lastId();
}

/**
 *
 * @return string 最后一条SQL
 */
function db_last_query() {
	return Lg\Database\Database::getConnection()->last_query();
}

/**
 *
 * @param string $table
 * @return \Lg\Database\Query
 */
function db_table($table) {
	return Lg\Database\Query::table($table);
}

function lg_log($messages, $level = 'system', $uid = 0) {
	$default_messages = [
		'level' => $level,
		'message' => \Lg\Support\Arr::get($messages, 'message'),
	];
	db_insert('logs', $default_messages);
}

function value($value) {
	return $value;
}

function import($library_path) {
	$segments = explode('.', $library_path);
	$module_name = Arr::get($segments, 0);
	if (is_null($module_name)) {
		return;
	}

	$filename = Arr::get($segments, 1, 'autoload');
	$filename = APP_PATH . '/' . $module_name . '/includes/' . $filename . '.php';
	if (file_exists($filename)) {
		include_once $filename;
	}
}

//date
function format_date($timestamp, $format = '', $timezone = NULL, $default = '') {
	if (empty($timestamp)) {
		return $default;
	}
	switch ($format) {
	case 'short':
		$format = __('date_format_short', 'Y-m-d');
		break;
	case 'long':
		$format = __('date_format_long', 'Y-m-d H:i:s');
		break;
	case '':
		$format = __('date_format_short', 'Y-m-d');
		break;
	}
	if (is_null($timezone)) {
		//date()
		return date($format, $timestamp);
	}
	$date_time = date_create('@' . $timestamp);
	if (!isset($timezone)) {
		$timezone = date_default_timezone_get();
	}
	date_timezone_set($date_time, new DateTimeZone($timezone));
	return date_format($date_time, $format);
}

function format_size($size, $langcode = NULL) {

}

//helpers

if (!function_exists('array_add')) {

	/**
	 * Add an element to an array using "dot" notation if it doesn't exist.
	 *
	 * @param  array   $array
	 * @param  string  $key
	 * @param  mixed   $value
	 * @return array
	 */
	function array_add($array, $key, $value) {
		return Arr::add($array, $key, $value);
	}

}
if (!function_exists('array_collapse')) {

	/**
	 * Collapse an array of arrays into a single array.
	 *
	 * @param  array  $array
	 * @return array
	 */
	function array_collapse($array) {
		return Arr::collapse($array);
	}

}
if (!function_exists('array_divide')) {

	/**
	 * Divide an array into two arrays. One with keys and the other with values.
	 *
	 * @param  array  $array
	 * @return array
	 */
	function array_divide($array) {
		return Arr::divide($array);
	}

}
if (!function_exists('array_dot')) {

	/**
	 * Flatten a multi-dimensional associative array with dots.
	 *
	 * @param  array   $array
	 * @param  string  $prepend
	 * @return array
	 */
	function array_dot($array, $prepend = '') {
		return Arr::dot($array, $prepend);
	}

}
if (!function_exists('array_except')) {

	/**
	 * Get all of the given array except for a specified array of items.
	 *
	 * @param  array  $array
	 * @param  array|string  $keys
	 * @return array
	 */
	function array_except($array, $keys) {
		return Arr::except($array, $keys);
	}

}
if (!function_exists('array_first')) {

	/**
	 * Return the first element in an array passing a given truth test.
	 *
	 * @param  array  $array
	 * @param  callable|null  $callback
	 * @param  mixed  $default
	 * @return mixed
	 */
	function array_first($array, callable $callback = null, $default = null) {
		return Arr::first($array, $callback, $default);
	}

}
if (!function_exists('array_flatten')) {

	/**
	 * Flatten a multi-dimensional array into a single level.
	 *
	 * @param  array  $array
	 * @param  int  $depth
	 * @return array
	 */
	function array_flatten($array, $depth = INF) {
		return Arr::flatten($array, $depth);
	}

}
if (!function_exists('array_forget')) {

	/**
	 * Remove one or many array items from a given array using "dot" notation.
	 *
	 * @param  array  $array
	 * @param  array|string  $keys
	 * @return void
	 */
	function array_forget(&$array, $keys) {
		return Arr::forget($array, $keys);
	}

}
if (!function_exists('array_get')) {

	/**
	 * Get an item from an array using "dot" notation.
	 *
	 * @param  \ArrayAccess|array  $array
	 * @param  string  $key
	 * @param  mixed   $default
	 * @return mixed
	 */
	function array_get($array, $key, $default = null) {
		return Arr::get($array, $key, $default);
	}

}
if (!function_exists('array_has')) {

	/**
	 * Check if an item or items exist in an array using "dot" notation.
	 *
	 * @param  \ArrayAccess|array  $array
	 * @param  string|array  $keys
	 * @return bool
	 */
	function array_has($array, $keys) {
		return Arr::has($array, $keys);
	}

}
if (!function_exists('array_last')) {

	/**
	 * Return the last element in an array passing a given truth test.
	 *
	 * @param  array  $array
	 * @param  callable|null  $callback
	 * @param  mixed  $default
	 * @return mixed
	 */
	function array_last($array, callable $callback = null, $default = null) {
		return Arr::last($array, $callback, $default);
	}

}
if (!function_exists('array_only')) {

	/**
	 * Get a subset of the items from the given array.
	 *
	 * @param  array  $array
	 * @param  array|string  $keys
	 * @return array
	 */
	function array_only($array, $keys) {
		return Arr::only($array, $keys);
	}

}
if (!function_exists('array_pluck')) {

	/**
	 * Pluck an array of values from an array.
	 *
	 * @param  array   $array
	 * @param  string|array  $value
	 * @param  string|array|null  $key
	 * @return array
	 */
	function array_pluck($array, $value, $key = null) {
		return Arr::pluck($array, $value, $key);
	}

}
if (!function_exists('array_prepend')) {

	/**
	 * Push an item onto the beginning of an array.
	 *
	 * @param  array  $array
	 * @param  mixed  $value
	 * @param  mixed  $key
	 * @return array
	 */
	function array_prepend($array, $value, $key = null) {
		return Arr::prepend($array, $value, $key);
	}

}
if (!function_exists('array_pull')) {

	/**
	 * Get a value from the array, and remove it.
	 *
	 * @param  array   $array
	 * @param  string  $key
	 * @param  mixed   $default
	 * @return mixed
	 */
	function array_pull(&$array, $key, $default = null) {
		return Arr::pull($array, $key, $default);
	}

}
if (!function_exists('array_set')) {

	/**
	 * Set an array item to a given value using "dot" notation.
	 *
	 * If no key is given to the method, the entire array will be replaced.
	 *
	 * @param  array   $array
	 * @param  string  $key
	 * @param  mixed   $value
	 * @return array
	 */
	function array_set(&$array, $key, $value) {
		return Arr::set($array, $key, $value);
	}

}
if (!function_exists('array_sort')) {

	/**
	 * Sort the array using the given callback.
	 *
	 * @param  array  $array
	 * @param  callable  $callback
	 * @return array
	 */
	function array_sort($array, callable $callback) {
		return Arr::sort($array, $callback);
	}

}
if (!function_exists('array_sort_recursive')) {

	/**
	 * Recursively sort an array by keys and values.
	 *
	 * @param  array  $array
	 * @return array
	 */
	function array_sort_recursive($array) {
		return Arr::sortRecursive($array);
	}

}
if (!function_exists('array_where')) {

	/**
	 * Filter the array using the given callback.
	 *
	 * @param  array  $array
	 * @param  callable  $callback
	 * @return array
	 */
	function array_where($array, callable $callback) {
		return Arr::where($array, $callback);
	}

}
if (!function_exists('camel_case')) {

	/**
	 * Convert a value to camel case.
	 *
	 * @param  string  $value
	 * @return string
	 */
	function camel_case($value) {
		return Str::camel($value);
	}

}
if (!function_exists('class_basename')) {

	/**
	 * Get the class "basename" of the given object / class.
	 *
	 * @param  string|object  $class
	 * @return string
	 */
	function class_basename($class) {
		$class = is_object($class) ? get_class($class) : $class;
		return basename(str_replace('\\', '/', $class));
	}

}
if (!function_exists('class_uses_recursive')) {

	/**
	 * Returns all traits used by a class, its subclasses and trait of their traits.
	 *
	 * @param  object|string  $class
	 * @return array
	 */
	function class_uses_recursive($class) {
		if (is_object($class)) {
			$class = get_class($class);
		}
		$results = [];
		foreach (array_merge([$class => $class], class_parents($class)) as $class) {
			$results += trait_uses_recursive($class);
		}
		return array_unique($results);
	}

}

if (!function_exists('data_fill')) {

	/**
	 * Fill in data where it's missing.
	 *
	 * @param  mixed   $target
	 * @param  string|array  $key
	 * @param  mixed  $value
	 * @return mixed
	 */
	function data_fill(&$target, $key, $value) {
		return data_set($target, $key, $value, false);
	}

}
if (!function_exists('data_get')) {

	/**
	 * Get an item from an array or object using "dot" notation.
	 *
	 * @param  mixed   $target
	 * @param  string|array  $key
	 * @param  mixed   $default
	 * @return mixed
	 */
	function data_get($target, $key, $default = null) {
		if (is_null($key)) {
			return $target;
		}
		$key = is_array($key) ? $key : explode('.', $key);
		while (!is_null($segment = array_shift($key))) {
			if ($segment === '*') {
				if ($target instanceof Collection) {
					$target = $target->all();
				} elseif (!is_array($target)) {
					return value($default);
				}
				$result = Arr::pluck($target, $key);
				return in_array('*', $key) ? Arr::collapse($result) : $result;
			}
			if (Arr::accessible($target) && Arr::exists($target, $segment)) {
				$target = $target[$segment];
			} elseif (is_object($target) && isset($target->{$segment})) {
				$target = $target->{$segment};
			} else {
				return value($default);
			}
		}
		return $target;
	}

}
if (!function_exists('data_set')) {

	/**
	 * Set an item on an array or object using dot notation.
	 *
	 * @param  mixed  $target
	 * @param  string|array  $key
	 * @param  mixed  $value
	 * @param  bool  $overwrite
	 * @return mixed
	 */
	function data_set(&$target, $key, $value, $overwrite = true) {
		$segments = is_array($key) ? $key : explode('.', $key);
		if (($segment = array_shift($segments)) === '*') {
			if (!Arr::accessible($target)) {
				$target = [];
			}
			if ($segments) {
				foreach ($target as &$inner) {
					data_set($inner, $segments, $value, $overwrite);
				}
			} elseif ($overwrite) {
				foreach ($target as &$inner) {
					$inner = $value;
				}
			}
		} elseif (Arr::accessible($target)) {
			if ($segments) {
				if (!Arr::exists($target, $segment)) {
					$target[$segment] = [];
				}
				data_set($target[$segment], $segments, $value, $overwrite);
			} elseif ($overwrite || !Arr::exists($target, $segment)) {
				$target[$segment] = $value;
			}
		} elseif (is_object($target)) {
			if ($segments) {
				if (!isset($target->{$segment})) {
					$target->{$segment} = [];
				}
				data_set($target->{$segment}, $segments, $value, $overwrite);
			} elseif ($overwrite || !isset($target->{$segment})) {
				$target->{$segment} = $value;
			}
		} else {
			$target = [];
			if ($segments) {
				data_set($target[$segment], $segments, $value, $overwrite);
			} elseif ($overwrite) {
				$target[$segment] = $value;
			}
		}
		return $target;
	}

}

if (!function_exists('e')) {

	/**
	 * Escape HTML special characters in a string.
	 *
	 * @param  string  $value
	 * @return string
	 */
	function e($value) {
		return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
	}

}
if (!function_exists('ends_with')) {

	/**
	 * Determine if a given string ends with a given substring.
	 *
	 * @param  string  $haystack
	 * @param  string|array  $needles
	 * @return bool
	 */
	function ends_with($haystack, $needles) {
		return Str::endsWith($haystack, $needles);
	}

}
if (!function_exists('head')) {

	/**
	 * Get the first element of an array. Useful for method chaining.
	 *
	 * @param  array  $array
	 * @return mixed
	 */
	function head($array) {
		return reset($array);
	}

}
if (!function_exists('last')) {

	/**
	 * Get the last element from an array.
	 *
	 * @param  array  $array
	 * @return mixed
	 */
	function last($array) {
		return end($array);
	}

}
if (!function_exists('object_get')) {

	/**
	 * Get an item from an object using "dot" notation.
	 *
	 * @param  object  $object
	 * @param  string  $key
	 * @param  mixed   $default
	 * @return mixed
	 */
	function object_get($object, $key, $default = null) {
		if (is_null($key) || trim($key) == '') {
			return $object;
		}
		foreach (explode('.', $key) as $segment) {
			if (!is_object($object) || !isset($object->{$segment})) {
				return value($default);
			}
			$object = $object->{$segment};
		}
		return $object;
	}

}
if (!function_exists('preg_replace_array')) {

	/**
	 * Replace a given pattern with each value in the array in sequentially.
	 *
	 * @param  string  $pattern
	 * @param  array   $replacements
	 * @param  string  $subject
	 * @return string
	 */
	function preg_replace_array($pattern, array $replacements, $subject) {
		return preg_replace_callback($pattern,
			function () use (&$replacements) {
				foreach ($replacements as $key => $value) {
					return array_shift($replacements);
				}
			}, $subject);
	}

}
if (!function_exists('retry')) {

	/**
	 * Retry an operation a given number of times.
	 *
	 * @param  int  $times
	 * @param  callable  $callback
	 * @param  int  $sleep
	 * @return mixed
	 */
	function retry($times, callable $callback, $sleep = 0) {
		$times--;
		beginning:
		try {
			return $callback();
		} catch (Exception $e) {
			if (!$times) {
				throw $e;
			}
			$times--;
			if ($sleep) {
				usleep($sleep * 1000);
			}
			goto beginning;
		}
	}

}
if (!function_exists('snake_case')) {

	/**
	 * Convert a string to snake case.
	 *
	 * @param  string  $value
	 * @param  string  $delimiter
	 * @return string
	 */
	function snake_case($value, $delimiter = '_') {
		return Str::snake($value, $delimiter);
	}

}
if (!function_exists('starts_with')) {

	/**
	 * Determine if a given string starts with a given substring.
	 *
	 * @param  string  $haystack
	 * @param  string|array  $needles
	 * @return bool
	 */
	function starts_with($haystack, $needles) {
		return Str::startsWith($haystack, $needles);
	}

}
if (!function_exists('str_contains')) {

	/**
	 * Determine if a given string contains a given substring.
	 *
	 * @param  string  $haystack
	 * @param  string|array  $needles
	 * @return bool
	 */
	function str_contains($haystack, $needles) {
		return Str::contains($haystack, $needles);
	}

}
if (!function_exists('str_finish')) {

	/**
	 * Cap a string with a single instance of a given value.
	 *
	 * @param  string  $value
	 * @param  string  $cap
	 * @return string
	 */
	function str_finish($value, $cap) {
		return Str::finish($value, $cap);
	}

}
if (!function_exists('str_is')) {

	/**
	 * Determine if a given string matches a given pattern.
	 *
	 * @param  string  $pattern
	 * @param  string  $value
	 * @return bool
	 */
	function str_is($pattern, $value) {
		return Str::is($pattern, $value);
	}

}
if (!function_exists('str_limit')) {

	/**
	 * Limit the number of characters in a string.
	 *
	 * @param  string  $value
	 * @param  int     $limit
	 * @param  string  $end
	 * @return string
	 */
	function str_limit($value, $limit = 100, $end = '...') {
		return Str::limit($value, $limit, $end);
	}

}
if (!function_exists('str_plural')) {

	/**
	 * Get the plural form of an English word.
	 *
	 * @param  string  $value
	 * @param  int     $count
	 * @return string
	 */
	function str_plural($value, $count = 2) {
		return Str::plural($value, $count);
	}

}
if (!function_exists('str_random')) {

	/**
	 * Generate a more truly "random" alpha-numeric string.
	 *
	 * @param  int  $length
	 * @return string
	 *
	 * @throws \RuntimeException
	 */
	function str_random($length = 16) {
		return Str::random($length);
	}

}
if (!function_exists('str_replace_array')) {

	/**
	 * Replace a given value in the string sequentially with an array.
	 *
	 * @param  string  $search
	 * @param  array   $replace
	 * @param  string  $subject
	 * @return string
	 */
	function str_replace_array($search, array $replace, $subject) {
		return Str::replaceArray($search, $replace, $subject);
	}

}
if (!function_exists('str_replace_first')) {

	/**
	 * Replace the first occurrence of a given value in the string.
	 *
	 * @param  string  $search
	 * @param  string  $replace
	 * @param  string  $subject
	 * @return string
	 */
	function str_replace_first($search, $replace, $subject) {
		return Str::replaceFirst($search, $replace, $subject);
	}

}
if (!function_exists('str_replace_last')) {

	/**
	 * Replace the last occurrence of a given value in the string.
	 *
	 * @param  string  $search
	 * @param  string  $replace
	 * @param  string  $subject
	 * @return string
	 */
	function str_replace_last($search, $replace, $subject) {
		return Str::replaceLast($search, $replace, $subject);
	}

}
if (!function_exists('str_singular')) {

	/**
	 * Get the singular form of an English word.
	 *
	 * @param  string  $value
	 * @return string
	 */
	function str_singular($value) {
		return Str::singular($value);
	}

}
if (!function_exists('str_slug')) {

	/**
	 * Generate a URL friendly "slug" from a given string.
	 *
	 * @param  string  $title
	 * @param  string  $separator
	 * @return string
	 */
	function str_slug($title, $separator = '-') {
		return Str::slug($title, $separator);
	}

}
if (!function_exists('studly_case')) {

	/**
	 * Convert a value to studly caps case.
	 *
	 * @param  string  $value
	 * @return string
	 */
	function studly_case($value) {
		return Str::studly($value);
	}

}
if (!function_exists('tap')) {

	/**
	 * Call the given Closure with the given value then return the value.
	 *
	 * @param  mixed  $value
	 * @param  callable  $callback
	 * @return mixed
	 */
	function tap($value, $callback) {
		$callback($value);
		return $value;
	}

}
if (!function_exists('title_case')) {

	/**
	 * Convert a value to title case.
	 *
	 * @param  string  $value
	 * @return string
	 */
	function title_case($value) {
		return Str::title($value);
	}

}
if (!function_exists('trait_uses_recursive')) {

	/**
	 * Returns all traits used by a trait and its traits.
	 *
	 * @param  string  $trait
	 * @return array
	 */
	function trait_uses_recursive($trait) {
		$traits = class_uses($trait);
		foreach ($traits as $trait) {
			$traits += trait_uses_recursive($trait);
		}
		return $traits;
	}

}
if (!function_exists('value')) {

	/**
	 * Return the default value of the given value.
	 *
	 * @param  mixed  $value
	 * @return mixed
	 */
	function value($value) {
		return $value instanceof Closure ? $value() : $value;
	}

}
if (!function_exists('windows_os')) {

	/**
	 * Determine whether the current environment is Windows based.
	 *
	 * @return bool
	 */
	function windows_os() {
		return strtolower(substr(PHP_OS, 0, 3)) === 'win';
	}

}
if (!function_exists('with')) {

	/**
	 * Return the given object. Useful for chaining.
	 *
	 * @param  mixed  $object
	 * @return mixed
	 */
	function with($object) {
		return $object;
	}

}

/**
 *
 * @param array $args
 * @param string|array $defaults
 * @return array
 */
function parse_args($args, $defaults = '') {
	if (is_object($args)) {
		$vars = get_object_vars($args);
	} elseif (is_array($args)) {
		$vars = &$args;
	} else {
		parse_str($args, $vars);
	}

	if (is_array($defaults)) {
		return array_merge($defaults, $vars);
	}

	return $vars;
}

function array_find_keys($keys, &$array) {
	$r = [];
	foreach ($keys as $key) {
		if (isset($array[$key])) {
			$r[$key] = $array[$key];
		}
	}
	return $r;
}

function array_unset_keys($keys, &$array) {
	foreach ($keys as $key) {
		if (isset($array[$key])) {
			unset($array[$key]);
		}
	}
	return $array;
}
